#!/usr/bin/perl

#=======================================================================
# git-rpull
# File ID: 8ed5e844-cf09-11e4-b7ff-000df06acc56
# Execute command in remote Git repositories
#
# Character set: UTF-8
# ©opyleft 2012– Øyvind A. Holm <sunny@sunbase.org>
# License: GNU General Public License version 2 or later, see end of 
# file for legal stuff.
#=======================================================================

use strict;
use warnings;
use Getopt::Long;

local $| = 1;

our $Debug = 0;

our %Opt = (

    'cmd' => '',
    'debug' => 0,
    'dry-run' => 0,
    'help' => 0,
    'local' => 0,
    'quiet' => 0,
    'verbose' => 0,
    'version' => 0,

);

our $progname = $0;
$progname =~ s/^.*\/(.*?)$/$1/;
our $VERSION = '0.00';

our $hostname = `hostname`;
chomp($hostname);

Getopt::Long::Configure('bundling');
GetOptions(

    'cmd|c=s' => \$Opt{'cmd'},
    'debug' => \$Opt{'debug'},
    'dry-run|n' => \$Opt{'dry-run'},
    'help|h' => \$Opt{'help'},
    'local|l' => \$Opt{'local'},
    'quiet|q+' => \$Opt{'quiet'},
    'verbose|v+' => \$Opt{'verbose'},
    'version' => \$Opt{'version'},

) || die("$progname: Option error. Use -h for help.\n");

$Opt{'debug'} && ($Debug = 1);
$Opt{'verbose'} -= $Opt{'quiet'};
$Opt{'help'} && usage(0);
if ($Opt{'version'}) {
    print_version();
    exit(0);
}

if (!length($Opt{'cmd'})) {
    die("$progname: -c/--cmd option not specified\n");
}

exit(main(%Opt));

sub main {
    # {{{
    my %Opt = @_;
    my $Retval = 0;

    if ($Opt{'local'}) {
        mysystem($Opt{'cmd'});
        $Opt{'verbose'} >= 0 && print("\n");
    }
    open(my $pipefp, 'git remote -v |') or die("$progname: git remote -v: Cannot open pipe: $!\n");
    my %done = ();
    while (my $curr = <$pipefp>) {
        if ($curr =~ /^(\S+)\s+(\S+)\s/) {
            my ($remote, $url) = ($1, $2);
            if ($url =~ /^(\S+)\@(\S+):(\S+)$/) {
                # The URL looks SSHish
                my ($username, $host, $userdir) = ($1, $2, $3);
                next if ($userdir =~ /\.git$/); # Avoid bare repos
                if (!defined($done{"$username$host$userdir"})) {
                    my @cmd = (
                        'ssh',
                        '-A',
                        "$username\@$host",
                        "cd $userdir && ($Opt{'cmd'})",
                    );
                    mysystem(@cmd);
                    $done{"$username$host$userdir"} = 1;
                    $Opt{'verbose'} >= 0 && print("\n");
                }
            } elsif (-d "$url/.") {
                if (!defined($done{"$url"}) && chdir($url)) {
                    next if (!-d ".git/."); # Avoid bare repos
                    msg(0, "($hostname) chdir $url");
                    mysystem($Opt{'cmd'});
                    $done{"$url"} = 1;
                    $Opt{'verbose'} >= 0 && print("\n");
                }
            }
        }
    }
    close($pipefp);

    return($Retval);
    # }}}
} # main()

sub check_sig {
    # {{{
    my $retval = shift;
    ($retval & 127) && die("\n$progname: Child process interrupted, aborting.\n");
    return(0);
    # }}}
} # check_sig()

sub mysystem {
    # {{{
    my @cmd = @_;
    msg(0, sprintf("(%s) %s '", $hostname, $Opt{'dry-run'} ? "Simulating" : "Executing") .
        join(" ", @cmd) . "'...");
    $? = 0;
    !$Opt{'dry-run'} && system(@cmd) && check_sig($?);
    return $?;
    # }}}
} # mysystem()

sub print_version {
    # Print program version {{{
    print("$progname v$VERSION\n");
    return;
    # }}}
} # print_version()

sub usage {
    # Send the help message to stdout {{{
    my $Retval = shift;

    if ($Opt{'verbose'}) {
        print("\n");
        print_version();
    }
    print(<<"END");

Usage: $progname [options] -c COMMAND

Execute commands in remote repositories via ssh. Scans all available 
remotes and checks if it looks SSHish. If it does, it logs in and 
executes the command specified with the -c/--cmd option in the 
directory.

Options:

  -c COMMAND, --cmd COMMAND
    Execute COMMAND in remote repositories.
  -h, --help
    Show this help.
  -l, --local
    Also execute the command in the local repository.
  -n, --dry-run
    Simulate, don't actually execute any commands.
  -q, --quiet
    Get less noisy. Can be repeated to increase quietness.
  -v, --verbose
    Increase level of verbosity. Can be repeated.
  --version
    Print version information.
  --debug
    Print debugging messages.

END
    exit($Retval);
    # }}}
} # usage()

sub msg {
    # Print a status message to stderr based on verbosity level {{{
    my ($verbose_level, $Txt) = @_;

    if ($Opt{'verbose'} >= $verbose_level) {
        print(STDERR "$progname: $Txt\n");
    }
    return;
    # }}}
} # msg()

sub D {
    # Print a debugging message {{{
    $Debug || return;
    my @call_info = caller;
    chomp(my $Txt = shift);
    my $File = $call_info[1];
    $File =~ s#\\#/#g;
    $File =~ s#^.*/(.*?)$#$1#;
    print(STDERR "$File:$call_info[2] $$ $Txt\n");
    return('');
    # }}}
} # D()

__END__

# Plain Old Documentation (POD) {{{

=pod

=head1 NAME



=head1 SYNOPSIS

 [options] [file [files [...]]]

=head1 DESCRIPTION



=head1 OPTIONS

=over 4

=item B<-h>, B<--help>

Print a brief help summary.

=item B<-v>, B<--verbose>

Increase level of verbosity. Can be repeated.

=item B<--version>

Print version information.

=item B<--debug>

Print debugging messages.

=back

=head1 BUGS



=head1 AUTHOR

Made by Øyvind A. Holm S<E<lt>sunny@sunbase.orgE<gt>>.

=head1 COPYRIGHT

Copyleft © Øyvind A. Holm E<lt>sunny@sunbase.orgE<gt>
This is free software; see the file F<COPYING> for legalese stuff.

=head1 LICENCE

This program is free software: you can redistribute it and/or modify it 
under the terms of the GNU General Public License as published by the 
Free Software Foundation, either version 2 of the License, or (at your 
option) any later version.

This program is distributed in the hope that it will be useful, but 
WITHOUT ANY WARRANTY; without even the implied warranty of 
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along 
with this program.
If not, see L<http://www.gnu.org/licenses/>.

=head1 SEE ALSO

=cut

# }}}

# vim: set fenc=UTF-8 ft=perl fdm=marker ts=4 sw=4 sts=4 et fo+=w :
